﻿/*
PainlessDAL Data Access Logic helper
Copyright (C) 2010  Eduardo de Britto Castro

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see http://www.gnu.org/licenses/. 
*/
using System.Collections.Generic;
using System.Data;
using System;

namespace PainlessDAL
{
    /// <summary>
    /// Data access logic utility class.
    /// </summary>
    /// <typeparam name="T">Persistent object type</typeparam>
    public static class DalHelper<T> where T : new()
    {
        #region Methods WITHOUT object factory
        /// <summary>
        /// Executes a SQL statement and returns a list of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="parametersPrefix">Ex: ?, @, :</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="values">Parameters values</param>
        /// <returns>List of persistent objects</returns>
        public static IEnumerable<T> GetList(DbContext context, string sql, string parametersPrefix, Action<object, T>[] fieldReaders, params object[] values)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql, parametersPrefix, values);
            return GetList(cmd, fieldReaders, null);
        }
        /// <summary>
        /// Executes a SQL statement and returns a list of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <returns>List of persistent objects</returns>
        public static IEnumerable<T> GetList(DbContext context, string sql, Action<object, T>[] fieldReaders)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql);
            return GetList(cmd, fieldReaders, null);
        }
        /// <summary>
        /// Executes a SQL statement and returns an instance of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="parametersPrefix">Ex: ?, @, :</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="values">Parameters values</param>
        /// <returns>An instance of persistent object</returns>
        public static T GetFirst(DbContext context, string sql, string parametersPrefix, Action<object, T>[] fieldReaders, params object[] values)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql, parametersPrefix, values);
            return GetFirst(cmd, fieldReaders, null);
        }
        /// <summary>
        /// Executes a SQL statement and returns an instance of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <returns>An instance of persistent object</returns>
        public static T GetFirst(DbContext context, string sql, Action<object, T>[] fieldReaders)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql);
            return GetFirst(cmd, fieldReaders, null);
        }
        /// <summary>
        /// Executes a SQL statement and loads values into an instance of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="parametersPrefix">Ex: ?, @, :</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="obj">Object instance</param>
        /// <returns>True if found. False if not found.</returns>
        public static bool GetFirst(DbContext context, string sql, string parametersPrefix, Action<object, T>[] fieldReaders, T obj, params object[] values)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql, parametersPrefix, values);
            return GetFirst(cmd, fieldReaders, obj);
        }
        /// <summary>
        /// Executes a SQL statement and loads values into an instance of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="obj">Object instance</param>
        /// <returns>True if found. False if not found.</returns>
        public static bool GetFirst(DbContext context, string sql, Action<object, T>[] fieldReaders, T obj)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql);
            return GetFirst(cmd, fieldReaders, obj);
        }
        #endregion

        #region Methods WITH object factory
        /// <summary>
        /// Executes a SQL statement and returns a list of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="parametersPrefix">Ex: ?, @, :</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="factory">A Func&lt;IDataReader, T&gt; for custom object creation.</param>
        /// <param name="values">Parameters values</param>
        /// <returns>List of persistent objects</returns>
        public static IEnumerable<T> GetList(DbContext context, string sql, string parametersPrefix, Action<object, T>[] fieldReaders, Func<IDataReader, T> factory, params object[] values)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql, parametersPrefix, values);
            return GetList(cmd, fieldReaders, factory);
        }
        /// <summary>
        /// Executes a SQL statement and returns a list of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="factory">A Func&lt;IDataReader, T&gt; for custom object creation.</param>
        /// <returns>List of persistent objects</returns>
        public static IEnumerable<T> GetList(DbContext context, string sql, Action<object, T>[] fieldReaders, Func<IDataReader, T> factory)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql);
            return GetList(cmd, fieldReaders, factory);
        }
        /// <summary>
        /// Executes a SQL statement and returns an instance of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="parametersPrefix">Ex: ?, @, :</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="factory">A Func&lt;IDataReader, T&gt; for custom object creation.</param>
        /// <param name="values">Parameters values</param>
        /// <returns>An instance of persistent object</returns>
        public static T GetFirst(DbContext context, string sql, string parametersPrefix, Action<object, T>[] fieldReaders, Func<IDataReader, T> factory, params object[] values)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql, parametersPrefix, values);
            return GetFirst(cmd, fieldReaders, factory);
        }
        /// <summary>
        /// Executes a SQL statement and returns an instance of persistent object
        /// </summary>
        /// <param name="context">DbContext</param>
        /// <param name="sql">SQL instruction. If, for example, parametersPrefix are "?p" then parameters must be named ?p0, ?p1...</param>
        /// <param name="fieldReaders">Field readers.</param>
        /// <param name="factory">A Func&lt;IDataReader, T&gt; for custom object creation.</param>
        /// <returns>An instance of persistent object</returns>
        public static T GetFirst(DbContext context, string sql, Action<object, T>[] fieldReaders, Func<IDataReader, T> factory)
        {
            IDbCommand cmd = DbHelper.CreateCommand(context, sql);
            return GetFirst(cmd, fieldReaders, factory);
        }
        #endregion

        private static IEnumerable<T> GetList(IDbCommand cmd, Action<object, T>[] fieldReaders, Func<IDataReader, T> factory)
        {
            using (IDataReader reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    T obj;
                    if (factory == null)
                        obj = new T();
                    else
                        obj = factory(reader);

                    for (int i = 0; i < fieldReaders.Length; i++)
                        fieldReaders[i](reader[i], obj);

                    yield return obj;
                }
            }
        }
        private static T GetFirst(IDbCommand cmd, Action<object, T>[] fieldReaders, Func<IDataReader, T> factory)
        {
            using (IDataReader reader = cmd.ExecuteReader())
            {
                if (reader.Read())
                {
                    T obj;
                    if (factory == null)
                        obj = new T();
                    else
                        obj = factory(reader);

                    for (int i = 0; i < fieldReaders.Length; i++)
                        fieldReaders[i](reader[i], obj);

                    return obj;
                }
                else
                    return default(T);
            }
        }
        private static bool GetFirst(IDbCommand cmd, Action<object, T>[] fieldReaders, T obj)
        {
            using (IDataReader reader = cmd.ExecuteReader())
            {
                if (reader.Read())
                {
                    for (int i = 0; i < fieldReaders.Length; i++)
                        fieldReaders[i](reader[i], obj);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }
}
